/* Copyright ©, 2023, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary
 * to ECARX (Hubei) Technology Co., Ltd. and/or other developers. No license to
 * any intellectual property rights contained within this file is either granted
 * or implied except as may be provided in an agreement with ECARX (Hubei)
 * Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole
 * or in part is prohibited.
 */

#define LOG_TAG "PKIServer"
#include "vendor/ecarx/xma/pki/1.0/types.h"

#include "PKIGrpcServer.h"
#include "PKIHal.h"
#include "PKIServer.grpc.pb.h"
#include "PKIServer.pb.h"
#include "grpcpp/impl/codegen/status.h"
#include "log/log_main.h"
#include <atomic>
#include <chrono>
#include <grpc++/grpc++.h>
#include <memory>
#include <thread>
#include <vector>

namespace vendor::ecarx::xma::pki::V1_0::implementation {

class PKIGrpcServerImpl : public PKIGrpcServer,
                          public pki_proto::PKIServer::Service {
public:
  PKIGrpcServerImpl() {ALOGE("PKIHalClient create");}

  ~PKIGrpcServerImpl() {
    mShutDownFlag.store(true);
    if (mServer) {
      mServer->Shutdown();
    }
  }
  void offer(PKIHal *pki_hal) override {
    ALOGE("%s in", __FUNCTION__);
    mPKIHal = pki_hal;
    if (mServer) {
      ALOGW("%s: GrpcPKIServer has already started.", __func__);
      return;
    }
    mPKIServerThread = std::make_unique<std::thread>([this]() {
      while (!mShutDownFlag.load()) {
        ::grpc::ServerBuilder builder;
        builder.RegisterService(this);
        builder.AddListeningPort("0.0.0.0:40006",
                                 ::grpc::InsecureServerCredentials());
        mServer = builder.BuildAndStart();
        if (mServer == nullptr) {
          ALOGE("%s:: failed to create the GRPC server, please make sure the "
                "configuration and permissions are correct",
                __func__);
          std::this_thread::sleep_for(std::chrono::milliseconds(100));
          continue;
        }
        ALOGW("%s: GrpcPKIServer has started.", __func__);
        mServer->Wait();
        mServer.reset();
      }
    });
  }
  ::grpc::Status getCertificate(::grpc::ServerContext *context,
                                const ::google::protobuf::Empty *request,
                                ::pki_proto::Certificate *response) override {
    ALOGE("%s in", __FUNCTION__);
    StatusCode statusCode;
    auto certificate = mPKIHal->readCertificate(&statusCode);
    if (certificate.status == CertStatus::AVAILABLE) {
      response->set_status(::pki_proto::CertStatus::AVAILABLE);
    } else {
      response->set_status(::pki_proto::CertStatus::UNAVAILABLE);
    }
    response->set_bytes_value(certificate.rawValue.data(),
                              certificate.rawValue.size());
    response->set_password(certificate.password);
    return ::grpc::Status::OK;
  }

  ::grpc::Status
  updateInstallStatus(::grpc::ServerContext *context,
                      const ::pki_proto::InstallResult *request,
                      ::google::protobuf::Empty *response) override {
    ALOGE("%s in", __FUNCTION__);
    auto client = request->client();
    auto installStatus = [](pki_proto::InstallStatus status) -> InstallStatus {
      if (status == pki_proto::InstallStatus::SUCCESS) {
        return InstallStatus::SUCCESS;
      } else if (status == pki_proto::InstallStatus::FAILED) {
        return InstallStatus::FAILED;
      } else if (status == pki_proto::InstallStatus::HAS_INSTALLED) {
        return InstallStatus::HAS_INSTALLED;
      }
      return InstallStatus::UNKNOW;
    };

    if (client == pki_proto::PKIInstallPlatform::SLAVE_AP) {
      mPKIHal->updatePKIInstallStatus(PKIInstallPlatform::SLAVE_AP,
                                      installStatus(request->status()));
    } else if (client == pki_proto::PKIInstallPlatform::MASTER_AP) {
      mPKIHal->updatePKIInstallStatus(PKIInstallPlatform::MASTER_AP,
                                      installStatus(request->status()));
    }
    return ::grpc::Status::OK;
  }

private:
  std::unique_ptr<::grpc::Server> mServer{nullptr};
  PKIHal *mPKIHal;
  std::unique_ptr<std::thread> mPKIServerThread;
  std::atomic_bool mShutDownFlag{false};
};

std::unique_ptr<PKIGrpcServer> makeGRPCServer() {
  return std::make_unique<PKIGrpcServerImpl>();
}
} // namespace vendor::ecarx::xma::pki::V1_0::implementation